﻿using OA.Infrastructure.DomainModels.TemporaryAuthorization;
using OA.Infrastructure.SignalR.Abstractions;
using OA.Infrastructure.WeCom;
using OA.Infrastructure.WeCom.Generator;
using Quartz;
using System.Text;

namespace OA.Infrastructure.Impl;

internal class UserSensitiveDataTemporaryAuthorizationService : ITemporaryAuthorizationService<TemporaryAuthorizationCodeModel>
{
    private readonly DbContext _context;

    private readonly TemporaryAuthorizationToolChains<TemporaryAuthorizationCodeModel> _toolChains;

    public UserSensitiveDataTemporaryAuthorizationService(
        DbContext context,
        IWeComService weComService,
        ISignalRService signalRService,
        ISchedulerFactory schedulerFactory)
    {
        _context = context;

        _toolChains = new(
            context,
            signalRService,
            weComService,
            schedulerFactory,
            ITemporaryAuthorizationService<TemporaryAuthorizationCodeModel>.s_request,
            "UserSensitiveDataTemporaryAuthorization",
            "UserSensitiveDataTemporaryAuthorizationCallback");
    }

    public async Task<string> TryAcquireAsync(TemporaryAuthorizationCodeModel model, CancellationToken cancellationToken)
    {
        var @operator = _context.CurrentUser();
        var key = Guid.NewGuid().ToString("N");

        model.Commiter = @operator;

        if (App.ManagedByDocker)
        {
            var card = _toolChains.DefaultTemplateCardEngine(model.Commiter)
                .AddHoriztontalContentItemForText("详情", model.RequireContent)
                .AddButtonForCallback("驳回", WeComCallbackEvents.TemplateCardUserTemporaryAuthRejectEventKey, WeComTemplateCardButtonStyle.Dangerous)
                .AddButtonForCallback("授权", WeComCallbackEvents.TemplateCardUserTemporaryAuthGrantEventKey, WeComTemplateCardButtonStyle.Primary)
                .Generate();

            await _toolChains.SendCardToWeComAsync(card, key);
        }

        _toolChains.TryAdd(key, model);

        await _toolChains.SendToApproverAsync(new TemporaryAuthorizationRecord
        {
            Commiter = @operator.Name,
            Description = model.RequireContent,
            Key = key
        }, cancellationToken);

        return key;
    }

    public bool TryConsume(string key, string code, out TemporaryAuthorizationCodeModel model)
    {
        var currentDate = DateTime.UtcNow;

        foreach (var item in _toolChains.Package())
        {
            if (item.Value.Expiration < currentDate)
            {
                _toolChains.Package().TryRemove(item);
            }
        }

        if (_toolChains.Package().TryGetValue(key, out model!))
        {
            if (model.Code is not null && model.Code.Equals(code, StringComparison.OrdinalIgnoreCase))
            {
                _toolChains.Package().TryRemove(key, out model!);

                return model.Expiration >= currentDate;
            }
        }

        return false;
    }

    public async Task<OperationalResult> GrantAsync(string key, CancellationToken cancellationToken)
    {
        if (_toolChains.TryExtractRequest(key, out var model))
        {
            if (model.Code is not null)
            {
                return OperationalResult.Fail("请求已被处理");
            }

            model.Code = GenerateRandomCode(6);
            model.GrantDate = DateTime.UtcNow;

            await _toolChains.GrantAsync(model, new TemporaryAuthorizationCallbackRecord
            {
                Status = OperationalResult.SUCCESS,
                Code = model.Code,
                Description = $"您的临时授权码：{model.Code}"
            }, cancellationToken);

            return OperationalResult.Ok();
        }

        return OperationalResult.Fail();
    }

    public async Task<OperationalResult> RejectAsync(string key, CancellationToken cancellationToken)
    {
        if (_toolChains.TryExtractRequest(key, out var model))
        {
            await _toolChains.RejectAsync(key, model, new TemporaryAuthorizationCallbackRecord
            {
                Status = OperationalResult.FAILURE,
                Description = $"您的临时授权申请（{model.RequireContent}）被驳回"
            }, cancellationToken);

            _toolChains.TryRemove(key, out _);

            return OperationalResult.Ok();
        }

        return OperationalResult.Fail();
    }


    private const string _baseCode = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    private static string GenerateRandomCode(int length)
    {
        var sb = new StringBuilder();

        for (var i = 0; i < length; i++)
        {
            sb.Append(_baseCode[Random.Shared.Next(_baseCode.Length)]);
        }

        return sb.ToString();
    }
}